package com.treasure.mall.biz.service.core.withdraw.impl;

import com.treasure.mall.biz.base.AbstractSearchableCrudService;
import com.treasure.mall.biz.base.SpecificationAndSort;
import com.treasure.mall.biz.bo.BaseConfig;
import com.treasure.mall.biz.dto.BaseSaveDTO;
import com.treasure.mall.biz.dto.BaseSearchDTO;
import com.treasure.mall.biz.dto.condition.withdraw.WithdrawSearchCondition;
import com.treasure.mall.biz.dto.user.ModifyBalanceDTO;
import com.treasure.mall.biz.dto.withdraw.UpdateWithdrawStatusDTO;
import com.treasure.mall.biz.dto.withdraw.WithdrawDTO;
import com.treasure.mall.biz.entity.user.UserPO;
import com.treasure.mall.biz.entity.user.UserPO_;
import com.treasure.mall.biz.entity.withdraw.UserBankInfoPO;
import com.treasure.mall.biz.entity.withdraw.WithdrawPO;
import com.treasure.mall.biz.entity.withdraw.WithdrawPO_;
import com.treasure.mall.biz.repository.withdraw.WithdrawRepository;
import com.treasure.mall.biz.service.core.BaseConfigService;
import com.treasure.mall.biz.service.core.user.MerchantService;
import com.treasure.mall.biz.service.core.user.UserService;
import com.treasure.mall.biz.service.core.withdraw.UserBankInfoService;
import com.treasure.mall.biz.service.core.withdraw.WithdrawService;
import com.treasure.mall.zbq.common.StringUtilsExt;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.log.AdvanceLogType;
import com.treasure.mall.zbq.common.ienum.withdraw.WithdrawEnums;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author zoul
 * @version 1.0
 * @date 2020/6/14 15:42
 * @Description
 */
@Service
public class WithdrawServiceImpl extends AbstractSearchableCrudService<WithdrawPO, Long> implements WithdrawService {

    @Autowired
    private WithdrawRepository withdrawRepository;
    @Autowired
    private BaseConfigService baseConfigService;
    @Autowired
    private UserService userService;
    @Autowired
    private UserBankInfoService userBankInfoService;
    @Autowired
    private MerchantService merchantService;

    protected WithdrawServiceImpl(WithdrawRepository repository) {
        super(repository);
    }

    @Override
    protected SpecificationAndSort<WithdrawPO> specificationAndSort(BaseSearchDTO searchDTO) {
        WithdrawSearchCondition condition = (WithdrawSearchCondition) searchDTO;
        Specification<WithdrawPO> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (condition.getWithdrawStatus() != -1) {
                predicates.add(cb.equal(root.get(WithdrawPO_.withdrawStatus), condition.getWithdrawStatus()));
            }

            if (condition.getWithdrawType() != null) {
                predicates.add(cb.equal(root.get(WithdrawPO_.withdrawType), condition.getWithdrawType()));
            }
            if (!org.springframework.util.StringUtils.isEmpty(condition.getUserNo())) {
                predicates.add(cb.equal(root.get(WithdrawPO_.userPo).get(UserPO_.mobile), condition.getUserNo()));
            }

            if (!org.springframework.util.StringUtils.isEmpty(condition.getBeginTime())) {
                LocalDateTime beginTime = StringUtilsExt.localDateTimeFormat(condition.getBeginTime());
                predicates.add(cb.greaterThanOrEqualTo(root.get(UserPO_.createTime), beginTime));
            }
            if (!org.springframework.util.StringUtils.isEmpty(condition.getEndTime())) {
                LocalDateTime endTime = StringUtilsExt.localDateTimeFormat(condition.getEndTime());
                predicates.add(cb.lessThanOrEqualTo(root.get(UserPO_.createTime), endTime));
            }

            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Sort sort = new Sort(Sort.Direction.DESC, "createTime");
        return new SpecificationAndSort<>(specification, sort);
    }

    @Override
    public WithdrawPO save(BaseSaveDTO saveDTO) {
        return null;
    }


    @Override
    public ApiResult applyWithdraw(WithdrawDTO dto, UserPO user) {
        BaseConfig config = baseConfigService.getConfig();
        LocalDateTime nowTime = LocalDateTime.now();
        WithdrawPO po = new WithdrawPO();
        //region 判断当月已经提现次数
//        LocalDateTime beginTime = date.with(TemporalAdjusters.firstDayOfMonth());
//        LocalDateTime endTime = date.plusMonths(1).with(TemporalAdjusters.firstDayOfMonth());
//        List<WithdrawEnums.WithdrawStatus> withdrawStatuses= new ArrayList<>();
//        withdrawStatuses.add(WithdrawEnums.WithdrawStatus.APPROVAL_ING);
//        withdrawStatuses.add(WithdrawEnums.WithdrawStatus.APPROVAL_SUCCESS);
//        withdrawStatuses.add(WithdrawEnums.WithdrawStatus.GRANT_SUCCESS);
//        withdrawRepository.countWithdrawNum(dto.getWithdrawType(),user.getUserId(),beginTime,endTime,withdrawStatuses);
        //endregion
        user = userService.findById(user.getUserId());
        if (dto.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_BALANCE) {
            if (dto.getApplyMoney().compareTo(config.getWithdrawConfig().getBalanceMinAmount()) < 0
                    || dto.getApplyMoney().compareTo(config.getWithdrawConfig().getBalanceMaxAmount()) > 0) {
                throw new ApiException("您申请的金额不满足最低或最高提现金额,请重新填写后申请!");
            }
            if (user.getBalance().compareTo(dto.getApplyMoney()) < 0) {
                throw new ApiException("您的余额小于您的提现金额,请重新填写后申请!");
            }
            po.setApplyFee(dto.getApplyMoney().multiply(config.getWithdrawConfig().getBalanceWithdrawRatio())
                    .divide(new BigDecimal(100L), 2, BigDecimal.ROUND_HALF_UP));
        }
        if (dto.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_GOODS) {
            if (dto.getApplyMoney().compareTo(config.getWithdrawConfig().getGoodsMinAmount()) < 0
                    || dto.getApplyMoney().compareTo(config.getWithdrawConfig().getGoodsMaxAmount()) > 0) {
                throw new ApiException("您申请的金额不满足最低或最高提现金额,请重新填写后申请!");
            }
            if (user.getMerchant().getBalance().compareTo(dto.getApplyMoney()) < 0) {
                throw new ApiException("您的货款小于您的提现金额,请重新填写后申请!");
            }
            po.setApplyFee(dto.getApplyMoney().multiply(config.getWithdrawConfig().getGoodsWithdrawRatio())
                    .divide(new BigDecimal(100L), 2, BigDecimal.ROUND_HALF_UP));
        }
        UserBankInfoPO bankPo = userBankInfoService.findById(dto.getInfoId());
        if (bankPo == null) {
            throw new ApiException("未找到该银行卡,请刷新后重试!");
        }
        if (!bankPo.getUserPo().getUserId().equals(user.getUserId())) {
            throw new ApiException("该银行卡不属于您,请刷新后重试!");
        }
        po.setApplyMoney(dto.getApplyMoney());
        po.setBankPo(bankPo);
        po.setBankName(bankPo.getBankPo().getBankName());
        po.setBranchBankName(bankPo.getBranchBankName());
        po.setBankNo(bankPo.getBankNo());
        po.setRealName(bankPo.getRealName());
        po.setUserPo(user);
        po.setWithdrawStatus(WithdrawEnums.WithdrawStatus.APPROVAL_ING);
        po.setWithdrawType(dto.getWithdrawType());
        po.setCreateTime(nowTime);
        po.setCreator(user.getMobile());
        po.setDeleted(false);
        po.setUpdateTime(nowTime);
        po = withdrawRepository.save(po);

        //region 扣除余额or货款
        ModifyBalanceDTO balanceDto = new ModifyBalanceDTO();
        balanceDto.setAmount(dto.getApplyMoney().negate());
        balanceDto.setOrderId(po.getApplyId().toString());
        balanceDto.setOpId(user.getUserId().intValue());
        balanceDto.setOpName(user.getUsername());
        balanceDto.setRemark("提现扣除");
        balanceDto.setId(user.getUserId());
        if (dto.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_BALANCE) {
            balanceDto.setLogType(AdvanceLogType.BALANCE_WITHDRAW);
            userService.modifyBalance(balanceDto);
        }
        if (dto.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_GOODS) {
            balanceDto.setLogType(AdvanceLogType.LOG_TYPE7);
            balanceDto.setId(user.getMerchantId());
            merchantService.modifyBalance(balanceDto);
        }
        //endregion
        return ApiResult.resultWith(ResultCodeEnum.SUCCESS);
    }

    /**
     * 修改提现申请状态
     *
     * @param dto 修改实体
     */
    @Override
    public void updateWithdrawStatus(UpdateWithdrawStatusDTO dto) {
        WithdrawPO drawPo = this.findById(dto.getApplyId());
        if (drawPo == null) {
            throw new ApiException("未找到该笔提现申请,请刷新后重试");
        }
        if (drawPo.getWithdrawStatus() == WithdrawEnums.WithdrawStatus.APPROVAL_ING) {
            if (!dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.APPROVAL_FAIL.getCode())
                    && !dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.APPROVAL_SUCCESS.getCode())) {
                throw new ApiException("该状态下无法进行该操作,请刷新后重试");
            }
        }
        if (drawPo.getWithdrawStatus() == WithdrawEnums.WithdrawStatus.APPROVAL_SUCCESS) {
            if (!dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.GRANT_FAIL.getCode())
                    && !dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.GRANT_SUCCESS.getCode())) {
                throw new ApiException("该状态下无法进行该操作,请刷新后重试");
            }
        }
        if (drawPo.getWithdrawStatus() == WithdrawEnums.WithdrawStatus.CANCEL) {
            throw new ApiException("该状态下无法进行该操作,请刷新后重试");
        }
        withdrawRepository.updateWithdrawStatus(WithdrawEnums.WithdrawStatus.values()[dto.getWithdrawStatus()], dto.getRefuseReason(), dto.getApplyId());

        if (dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.APPROVAL_FAIL.getCode())
                || dto.getWithdrawStatus().equals(WithdrawEnums.WithdrawStatus.GRANT_FAIL.getCode())) {
            //region 扣除余额or货款
            ModifyBalanceDTO balanceDto = new ModifyBalanceDTO();
            balanceDto.setAmount(drawPo.getApplyMoney());
            balanceDto.setOrderId(drawPo.getApplyId().toString());
            balanceDto.setOpId(dto.getOpId());
            balanceDto.setOpName(dto.getOpName());
            balanceDto.setRemark("提现失败退回");
            balanceDto.setId(drawPo.getUserPo().getUserId());
            if (drawPo.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_BALANCE) {
                balanceDto.setLogType(AdvanceLogType.WITHDRAWAL_FAILED_BACK);
                userService.modifyBalance(balanceDto);
            }
            if (drawPo.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_GOODS) {
                balanceDto.setLogType(AdvanceLogType.LOG_TYPE8);
                balanceDto.setId(drawPo.getUserPo().getMerchantId());
                merchantService.modifyBalance(balanceDto);
            }
            //endregion
        }
    }

    /**
     * 用户取消提现申请
     *
     * @param userId  用户Id
     * @param applyId 提现Id
     */
    @Override
    public ApiResult cancelWithdrawStatus(Long userId, Long applyId) {
        WithdrawPO drawPo = this.findById(applyId);
        if (drawPo == null || !drawPo.getUserPo().getUserId().equals(userId)) {
            throw new ApiException("未找到该笔提现申请,请刷新后重试");
        }
        if (drawPo.getWithdrawStatus() != WithdrawEnums.WithdrawStatus.APPROVAL_ING) {
            throw new ApiException("该状态下无法进行取消,请刷新后重试");
        }
        String reason = "用户取消申请";
        withdrawRepository.updateWithdrawStatus(WithdrawEnums.WithdrawStatus.CANCEL, reason, applyId);
        UserPO userPo = userService.findById(userId);
        //region 扣除余额or货款
        ModifyBalanceDTO balanceDto = new ModifyBalanceDTO();
        balanceDto.setAmount(drawPo.getApplyMoney());
        balanceDto.setOrderId(drawPo.getApplyId().toString());
        balanceDto.setOpId(userPo.getUserId().intValue());
        balanceDto.setOpName(userPo.getUsername());
        balanceDto.setRemark("用户取消提现退回");
        if (drawPo.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_BALANCE) {
            balanceDto.setLogType(AdvanceLogType.CANCEL_BALANCE_WITHDRAW);
            balanceDto.setId(drawPo.getUserPo().getUserId());
            userService.modifyBalance(balanceDto);
        }
        if (drawPo.getWithdrawType() == WithdrawEnums.WithdrawType.WITHDRAW_GOODS) {
            balanceDto.setLogType(AdvanceLogType.LOG_TYPE12);
            balanceDto.setId(drawPo.getUserPo().getMerchantId());
            merchantService.modifyBalance(balanceDto);
        }
        //endregion
        return ApiResult.resultWith(ResultCodeEnum.SUCCESS);
    }

    /**
     * 修复提现老数据
     */
    @Override
    public void repairOldData() {
        try {
            List<WithdrawPO> pos = withdrawRepository.findAllByBankNoIsNull();
            if (pos != null && pos.size() > 0) {
                for (WithdrawPO po : pos) {
                    po.setRealName(po.getBankPo().getRealName());
                    po.setBankNo(po.getBankPo().getBankNo());
                    po.setBranchBankName(po.getBankPo().getBranchBankName());
                    po.setBankName(po.getBankPo().getBankPo().getBankName());
                    withdrawRepository.save(po);
                }
            }
        } catch (Exception ignored) {

        }
    }

}